const Koa = require('koa');
const { koaBody } = require('koa-body');
import * as Router from 'koa-router';
import * as staticFiles from 'koa-static';
import * as path from 'path';
import 'reflect-metadata';
const cors = require('koa2-cors'); // 跨域插件
const mysql = require('mysql2/promise');
const koaJwt = require('koa-jwt');
import routerResponse from './middle/response';
import { noLoginWhiteList } from './config';
import { JWT_SECRET } from './constants'; // token 处理
// 日志
const Axe = require('axe');
const Cabin = require('cabin');
const { Signale } = require('signale');

import { redisClientInit } from "./database/redisOperate";

import UserRoutes from './router/user-routes';
import InfoRoutes from './router/info-routes';
import AnimationRoutes from './router/animation-routes';
import PictureRoutes from './router/picture-routes';
import MenuRoutes from './router/menu-routes';
import BannerRoutes from './router/banner-routes';
import AppletConfigRoutes from './router/applet-config-routes';
import UserTempRoutes from './router/user-temp-routes';
import UploadLogRoutes from './router/upload-log-routes';
import LoginLogRoutes from './router/login-log-routes';
import RoleRoutes from './router/role-routes';

// initialize a new instance of Axe
const env = process.env.NODE_ENV;
const logger = new Axe({
  logger: new Signale(),
  level: env === 'production' ? 'error' : 'info'
});
const cabin = new Cabin({ logger });

// 解决类型检测的警告
declare module 'koa' {
  interface Request {
    body: any;
    files: any;
  }
}

const app = new Koa();

// 创建mysql数据库连接池
const dbPool = mysql.createPool({
  host: process.env['HOST_MYSQL'],
  port: process.env['PORT_MYSQL'],
  user: process.env['USER_MYSQL'],
  password: process.env['PASSWORD_MYSQL'],
  database: process.env['DATABASE_MYSQL'],
  waitForConnections: true,      // 当没有可用连接时，新来的连接请求是否等待
  connectionLimit: 10,         // 连接池中最大连接数量
  queueLimit: 0,               // 连接池队列中最大请求数量，0表示无限制
  idleTimeout: 60 * 1000,          // 一个连接空闲多久后被释放，单位毫秒
  connectTimeout: 5 * 1000,       // 连接超时时间，单位毫秒
});
app.context.dbPool = dbPool; // 将数据库连接对象挂载到koa上下文中

// 初始化redis数据库的连接池
redisClientInit();

// create router
const router = new Router();
UserRoutes.forEach(route => router[route.method](route.path, route.action));
InfoRoutes.forEach(route => router[route.method](route.path, route.action));
AnimationRoutes.forEach(route => router[route.method](route.path, route.action));
PictureRoutes.forEach(route => router[route.method](route.path, route.action));
MenuRoutes.forEach(route => router[route.method](route.path, route.action));
AppletConfigRoutes.forEach(route => router[route.method](route.path, route.action));
BannerRoutes.forEach(route => router[route.method](route.path, route.action));
UserTempRoutes.forEach(route => router[route.method](route.path, route.action));
UploadLogRoutes.forEach(route => router[route.method](route.path, route.action));
LoginLogRoutes.forEach(route => router[route.method](route.path, route.action));
RoleRoutes.forEach(route => router[route.method](route.path, route.action));

// use log
app.use(cabin.middleware);

// [记录]：koa2 中间件的顺序
// 先 koaBody 来解析请求体；
// 为响应添加方法；
// 处理接口是否允许跨域访问（必须放在jwt的前面，不然jwt验证失败就不会往后走了）；
// 添加错误拦截，比如：token权限问题返回401；
// jwt 验证用户的权限（错误会忘上抛）；
// 最后匹配服务路由，走接口内容；

app.use(staticFiles(path.join(__dirname, "../public"))); // 设置静态资源路径
app.use(koaBody({
  multipart: true, // 配置可以获取form data数据
  // 自动上传保存图片在服务器，保存后读取图片的时候需要
  formidable: {
    uploadDir: "./uploads",
    keepExtensions: true,
    maxFieldsSize: 20 * 1024 * 1024 // 10MB
  },
}));

app.use(routerResponse());

app.use(cors({
  origin: function(ctx: any) {
    // 设置允许来自指定域名请求，不能使用 localhost
    const whiteList = ["http://localhost:8280", "http://127.0.0.1:8280", "http://www.dangxiajing.cn:8080", "http://139.159.249.121:8088", "https://servicewechat.com", "http://127.0.0.1:3000"];
    const origin = ctx.request.header.origin;
    if (whiteList.includes(origin)) {
      return origin;
    }
  },
  exposeHeaders: ['Content-Length', 'Content-Type', 'Authorization'],
  maxAge: 5, //指定本次预检请求的有效期，单位为秒。
  credentials: true, // 是否允许发送Cookie，当设置为 true 时，
  allowMethods: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE'], //设置所允许的HTTP请求方法
  allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //设置服务器支持的所有头信息字段
}));

app.use(async (ctx, next) => {
  await next().catch((err)=>{
    if (401 === err.status) {
      ctx.status = 401;
      ctx.body = {
        data: null,
        msg: "请先登录",
        status: "fail",
        code: -1,
      };
    } else {
      console.log(err);
      throw err;
    }
  });
}); // 自定义中间件

app.use(koaJwt({ secret: JWT_SECRET }).unless((ctx) => {
  const currentRequest = { path: ctx.path, method: ctx.method };
  return noLoginWhiteList.some(item => item.reg.test(currentRequest.path));
}));

app.use(router.routes()); // 注册路由
app.use(router.allowedMethods()); // 添加路由请求的方式校验，get请求不能使用post请求方式

// add a listen.
module.exports = app.listen(process.env.PORT, () => {
  console.log('server is running at http://127.0.0.1:' + process.env.PORT);
});
